這三天的實作內容在第三天才有結果
這真的跟我想的不太一樣啊啊啊啊啊!!!!
好不容易有的結果最主要還歸功於第三天的 果斷放棄使用TFRecord
什麼鬼? 好爛! 我怎麼能甘心接受呢?
俗話說的好,在哪裡跌倒在哪裡 哭 (喂!
今天來個抖M上身,直接裸身踩TFRecord這個坑
讓我們開踩吧 !
TFRecord是一種專為Tensorflow打造的儲存文件的格式,就像是.csv、.md、.pkl、.npy、.json、...等等,他們會有自己的儲存方式,但這種儲存資料的檔案不外乎有一個特點,資料型態大致就那幾種 :
.csv、.md或是.json都是這種,就是用我們人能夠讀懂的資料儲存.pkl、.npy、TFRecord
那麼上面有提到TFRecord屬於一種binary的儲存格式,那就一定要來比一比binary格式常使用於python的比較吧!
| 格式 | 儲存速度 | 讀取速度 | 檔案大小 | 
|---|---|---|---|
| .npy | 最快 | 最快 | 最小 | 
| .pkl | 中 | 最慢 | 最大 | 
| .tfrecord | 最慢 | 中 | 中 | 
說起這個.npy的儲存格式,只適用於python的numpy來儲存和讀取,但非常的穩,也是我最喜歡的格式,簡單好用還非常穩定,看著統計圖果不其然是這三者中的佼佼者。反觀雖然.pkl是python的特殊儲存格式,但無論各方面都輸.npy。但有別於npy檔,pkl在儲存非array型的資料比numpy強,較不侷限。所以綜合來說,在對Numpy array熟悉的人來說,選擇.npy是比較好的,但在特殊的情況下,還是可以用.pkl。
以上就是簡單的比較。
等...等一下,是不是忘了什麼? 我們大TFRecord呢?
沒啥好講的啊,就很中規中矩啊。
不...不是吧? 他不是這裡面最困難使用的嗎? 而且還是儲存最慢的耶~
照你這麼講,它豈不比.pkl還爛嗎?
別急,我們先來談談TFRecord的讀取方式
比較於.npy,一般情況如.npy一樣,是將整個資料讀進內存
.npy要被讀入,你的內存空間就要至少30GB。
 
 
請原諒我的美術+小畫家,這不是重點
當然內部的細節交由Google來做,我們只負責使用。
所以接下來讓我們來看看要怎麼用吧!
feature的方式儲存陣列資料,且只有以下三種格式
string
byte
float
double
bool
enum
int32
uint32
int64
uint64
.SerializeToString序列化為binary-stringtf.Example建構範例feature,即可避免冗長的程式碼
# The number of observations in the dataset.
n_observations = int(1e4)
# Boolean feature, encoded as False or True.
feature0 = np.random.choice([False, True], n_observations)
# Integer feature, random from 0 to 4.
feature1 = np.random.randint(0, 5, n_observations)
# String feature
strings = np.array([b'cat', b'dog', b'chicken', b'horse', b'goat'])
feature2 = strings[feature1]
# Float feature, from a standard normal distribution
feature3 = np.random.randn(n_observations)
def serialize_example(feature0, feature1, feature2, feature3):
    """
    Creates a tf.Example message ready to be written to a file.
    """
    # Create a dictionary mapping the feature name to the tf.Example-compatible
    # data type.
    feature = {
        'feature0': _int64_feature(feature0),
        'feature1': _int64_feature(feature1),
        'feature2': _bytes_feature(feature2),
        'feature3': _float_feature(feature3),
    }
    # Create a Features message using tf.train.Example.
    example_proto = tf.train.Example(features=tf.train.Features(feature=feature))
    return example_proto.SerializeToString()
example_observation = []
serialized_example = serialize_example(False, 4, b'goat', 0.9876)
serialized_example
tf.Example,則可以參考下方程式碼
serialize_example後直接儲存成檔# Write the `tf.Example` observations to the file.
with tf.io.TFRecordWriter(filename) as writer:
  for i in range(n_observations):
    example = serialize_example(feature0[i], feature1[i], feature2[i], feature3[i])
    writer.write(example)
filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
take讀出for raw_record in raw_dataset.take(1):
  example = tf.train.Example()
  example.ParseFromString(raw_record.numpy())
  print(example)
tf.dataAPI中的使用tf.data是一個tensorflow的高階API,其方便和強大在於訓練過程中對輸入資料的優化!!
from_tensor_slices方法,就是告訴API接下來資料要從tensorf片一片一片讀進來features_dataset = tf.data.Dataset.from_tensor_slices((feature0, feature1, feature2, feature3))
features_dataset
take一個一個拿出來for f0,f1,f2,f3 in features_dataset.take(1):
    print(f0)
    print(f1)
    print(f2)
    print(f3)
tf.data.Dataset.map使用
graph mode下的tensorflow才能使用若是在
eager模式下,則要透過tf.py_function讓它在python中計算def tf_serialize_example(f0,f1,f2,f3): tf_string = tf.py_function( serialize_example, (f0,f1,f2,f3), # pass these args to the above function. tf.string) # the return type is `tf.string`. return tf.reshape(tf_string, ()) # The result is a scalar
tf_serialize_example(f0,f1,f2,f3)的輸出就是tf.Tensor了
tf.Tensor後使用tf.data.Dataset.map
serialized_features_dataset = features_dataset.map(serialize_example)
# serialized_features_dataset = features_dataset.map(tf_serialize_example)
serialized_features_dataset
TFRecord
filename = 'test.tfrecord'
writer = tf.data.experimental.TFRecordWriter(filename)
writer.write(serialized_features_dataset)
filenames = [filename]
raw_dataset = tf.data.TFRecordDataset(filenames)
raw_dataset
tf.dataAPI功能(consuming_tfrecord_data)
eager模式下進行(reading_a_tfrecord_file)filenames = tf.placeholder(tf.string, shape=[None])
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...)  # Parse the record into tensors.
dataset = dataset.repeat()  # Repeat the input indefinitely.
dataset = dataset.batch(32)
iterator = dataset.make_initializable_iterator()
# You can feed the initializer with the appropriate filenames for the current
# phase of execution, e.g. training vs. validation.
# Initialize `iterator` with training data.
training_filenames = ["/var/data/file1.tfrecord", "/var/data/file2.tfrecord"]
sess.run(iterator.initializer, feed_dict={filenames: training_filenames})
# Initialize `iterator` with validation data.
validation_filenames = ["/var/data/validation1.tfrecord", ...]
sess.run(iterator.initializer, feed_dict={filenames: validation_filenames})
Dataset.map()(preprocessing_data_with_datasetmap)我辛辛苦苦寫了許久,但你們肯定都只看這邊 嗚嗚嗚
python
tf.data
graph modeeager modetf.Example
序列副程式(範例的serialize_example function)| TFRecord | python | tf.data eager | tf.data graph | 備註 | 
|---|---|---|---|---|
| write | 用for透過 序列副程式寫入 | 透過 tf.py_function轉換為序列,map導向序列副程式,透過yield生成序列資料集,使用TFRecordWriter寫入 | map導向序列副程式,透過yield生成序列資料集,使用TFRecordWriter寫入 | |
| read | 透過 ParseFromString解碼,使用for和take讀取 | TFRecordDataset導入,直接透過take讀取 | TFRecordDataset導入,map導向_parse_function,透過take讀取 | tf.data只讀取:consuming_tfrecord_data、tf.data讀取且要顯示:reading_a_tfrecord_file、多通道資料:preprocessing_data_with_datasetmap | 
我前幾篇實作的確有踩到坑,總之我認為
tf.data,最主要原因我們有時候希望shuffle資料或是一些設定。補充一下,表格中整理的tf.data讀取資料都來自讀取且要顯示那個網址,但實際貼的程式碼來自只讀取的方法
我原本以為沒有實作的一天是很輕鬆的,但我錯了Orz
Using TFRecords and tf.Example
Importing Data